package org.teremail.message.builder;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.teremail.message.ContentHeaders;
import org.teremail.message.ContentHeadersImpl;
import org.teremail.panto.ContentDisposition;
import org.teremail.panto.ContentHeader;
import org.teremail.panto.ContentTransferEncoding;
import org.teremail.panto.ContentType;
import org.teremail.panto.ParseException;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;

public class ContentHeadersBuilder {
    
    private final Map<String,ContentHeader> headers = Maps.newLinkedHashMap();
    private boolean hasChanged = false;

    public ContentHeadersBuilder(ContentHeaders header) {
        // TODO: Think about performance here.
        load(header.toString());
    }

    public ContentHeadersBuilder() {
        hasChanged = true;
    }
    
    public ContentHeadersBuilder set(ContentHeader ch) {
        headers.put(ch.getName(), ch);
        hasChanged = true;
        return this;
    }

    public ContentHeadersBuilder setContentType(String type, String subType) {
        return set(new ContentType(type, subType));        
    }
    
    public ContentHeadersBuilder setContentType(String type, String subType,
            Map<String, String> params) {
        return set(new ContentType(null, type, subType, params));
    }

    public ContentHeadersBuilder setContentDisposition(String type, Map<String, String> cdp) {
        return set(new ContentDisposition(type, cdp));
    }

    public ContentHeadersBuilder setContentTransferEncoding(String type) {
        Map<String,String> params = ImmutableMap.of();
        return set(new ContentTransferEncoding(null, type, null, params));
    }
    

    public ContentHeaders build() {
        return new ContentHeadersImpl(Collections.unmodifiableMap(headers));
    }

    /**
     * Loads all of the headers from a string.  If the string is null we do
     * nothing.  This will clear out all of the existing headers first.
     * 
     * @param headerStr
     */
    public void load(String headerStr) {
        
        headers.clear();
        
        if (headerStr != null) {
            String[] lines = headerStr.split("\r\n");
            
            StringBuilder sb = null;
            
            for (String s : lines) {
                if (s.trim().length() != 0) {
                    if (s.startsWith(" ") || s.startsWith("\t")) {
                        if (sb != null) {
                            // Maintain the newline.
                            sb.append("\r\n");
                            sb.append(s);
                        } else {
                            throw new MalformedHeaderException("Malformed Header: " + s);
                        }
                    } else {
                        if (sb == null) {
                            sb = new StringBuilder();
                        } else {
                            addHeader(sb.toString());
                            sb = new StringBuilder();
                        }
                        sb.append(s);
                    }
                }
            }
            if (sb != null) {
                addHeader(sb.toString());
            }
        }
    }

    private void addHeader(String line) {
        ContentHeader ch = parseHeader(line);
        headers.put(ch.getName(), ch);
    }

    private ContentHeader parseHeader(String line) {
        ContentHeader ch = null;
        try {
            ch = ContentType.parse(line);
            if (ch == null) {
                ch = ContentDisposition.parse(line);
                if (ch == null) {
                    ch = ContentTransferEncoding.parse(line);
                    if (ch == null ){
                        ch = GenericContentHeader.parse(line);
                        if (ch == null) {
                            throw new MalformedHeaderException(line);
                        }
                    }
                }
            }
        } catch (ParseException e) {
            throw new MalformedHeaderException(line);
        }
        return ch;
    }
    
    private static class GenericContentHeader extends ContentHeader {

        private final String name;
        
        private final static Pattern PATTERN = 
            Pattern.compile("([^:]*):(.*)", Pattern.DOTALL | Pattern.CASE_INSENSITIVE);

        protected GenericContentHeader(String text, String name, String value) {
            super(text, value.trim(), null, new HashMap<String, String>());
            this.name = name;
        }
        
        public static GenericContentHeader parse(String line) throws ParseException {
            Matcher m = PATTERN.matcher(line);
            if (m.matches() && m.groupCount() == 2) {
                return new GenericContentHeader(line, m.group(1), m.group(2));
            } else {
                return null;
            }
            
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public String getDefaultParameter(String key) {
            return null;
        }
    }

    public boolean hasChanged() {
        return hasChanged;
    }

    public void add(String fieldData) {
        addHeader(fieldData);
    }

}
